#include "QueryPreprocessor.h"

#include "PKB.h"

QueryPreprocessor::QueryPreprocessor()
{
	/*
	heuristics_switch=0;
	ifstream cfgInFile("program_config.txt");
	string temp3;
	getline(cfgInFile,temp3,';');
	heuristics_switch = atoi(temp3.c_str());
	cfgInFile.close();
	*/
	ifstream inFile("heuristics.txt");
	string temp1;
	string temp2;
	while (inFile)
	{
		getline(inFile,temp1,';');
		getline(inFile,temp2,'\n');
		
		//if (heuristics_switch==0)
		//{
			cout << "value set not 0" << endl;
			_heuristics[temp1]=atoi(temp2.c_str());
		//}
		
		//else
		/*{
			cout << "value set 0" << endl;
			_heuristics[temp1]=0;
		}*/
	}

	inFile.close();
}

void QueryPreprocessor::calculateHeuristicsValue()
{
	MyTimer t;
	t.process();
	vector<string> relation = t.returnRelation();
	vector<unsigned __int64> result = t.returnResult();
	for (int i=0; i<relation.size();i++)
	{
		_heuristics[relation[i]]=result[i];
	}
}


QueryPreprocessor::~QueryPreprocessor()
{

}

unsigned __int64 QueryPreprocessor::ScoreRelation(query_relation& relation)
{
	unsigned __int64 value=0;
	int left;
	int right;
	if (relation.type==RelationType::WITH)
	{
		if (relation.parameters[0].type==ParameterType::NAME||relation.parameters[1].type==ParameterType::NAME)
		{
			value=0;
		}
		else
		{
			value=std::numeric_limits<unsigned __int64>::max();
		}
	}
	else if ((relation.type==RelationType::NEXTBIP)
		||(relation.type==RelationType::NEXTBIP_T)
		||(relation.type==RelationType::AFFECTSBIP)
		||(relation.type==RelationType::AFFECTSBIP_T))
	{
		value=std::numeric_limits<unsigned __int64>::max()-1;
	}
	else if (relation.type==RelationType::CALLS)
	{
		
		if (relation.parameters[0].type==ParameterType::ANY)
		{
			left = PKB::procTable.GetAllProcIndex().size();
		}
		else if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}
		else if (relation.parameters[0].type==ParameterType::NAME)
		{
			left = 1;
		}

		if (relation.parameters[1].type==ParameterType::ANY)
		{
			right = PKB::procTable.GetAllProcIndex().size();
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::NAME)
		{
			right = 1;
		}
		value = _heuristics["CALLS"]*left*right;
	}
	else if (relation.type==RelationType::CALLS_T)
	{
		
		if (relation.parameters[0].type==ParameterType::ANY)
		{
			left = PKB::shortcutTable.GetAllCall().size();
		}
		else if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}
		else if (relation.parameters[0].type==ParameterType::NAME)
		{
			left = 1;
		}

		if (relation.parameters[1].type==ParameterType::ANY)
		{
			right = PKB::shortcutTable.GetAllCall().size();
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::NAME)
		{
			right = 1;
		}
		value = _heuristics["CALLS_T"]*left*right;
	}
	else if (relation.type==RelationType::FOLLOWS)
	{
		
		if (relation.parameters[0].type==ParameterType::ANY)
		{
			left = PKB::shortcutTable.GetAllStatement().size();
		}
		else if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}
		else if (relation.parameters[0].type==ParameterType::LINE)
		{
			left = 1;
		}

		if (relation.parameters[1].type==ParameterType::ANY)
		{
			right = PKB::shortcutTable.GetAllStatement().size();
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::LINE)
		{
			right = 1;
		}
		value = _heuristics["FOLLOWS"]*left*right;
	}
	else if (relation.type==RelationType::FOLLOWS_T)
	{
		
		if (relation.parameters[0].type==ParameterType::ANY)
		{
			left = PKB::shortcutTable.GetAllStatement().size();
		}
		else if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}
		else if (relation.parameters[0].type==ParameterType::LINE)
		{
			left = 1;
		}

		if (relation.parameters[1].type==ParameterType::ANY)
		{
			right = PKB::shortcutTable.GetAllStatement().size();
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::LINE)
		{
			right = 1;
		}
		value = _heuristics["FOLLOWS_T"]*left*right;
	}
	else if (relation.type==RelationType::SIBLING)
	{
		
		if (relation.parameters[0].type==ParameterType::ANY)
		{
			left = PKB::shortcutTable.GetAllStatement().size();
		}
		else if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}
		else if (relation.parameters[0].type==ParameterType::LINE)
		{
			left = 1;
		}

		if (relation.parameters[1].type==ParameterType::ANY)
		{
			right = PKB::shortcutTable.GetAllStatement().size();
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::LINE)
		{
			right = 1;
		}
		value = _heuristics["SIBLING"]*left*right;
	}
	else if (relation.type==RelationType::PARENT)
	{
		
		if (relation.parameters[0].type==ParameterType::ANY)
		{
			left = PKB::shortcutTable.GetAllStatement().size();
		}
		else if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}
		else if (relation.parameters[0].type==ParameterType::LINE)
		{
			left = 1;
		}

		if (relation.parameters[1].type==ParameterType::ANY)
		{
			right = PKB::shortcutTable.GetAllStatement().size();
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::LINE)
		{
			right = 1;
		}
		value = _heuristics["PARENT"]*left*right;
	}
	else if (relation.type==RelationType::PARENT_T)
	{
		
		if (relation.parameters[0].type==ParameterType::ANY)
		{
			left = PKB::shortcutTable.GetAllIf().size()+PKB::shortcutTable.GetAllWhile().size();
		}
		else if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}
		else if (relation.parameters[0].type==ParameterType::LINE)
		{
			left = 1;
		}

		if (relation.parameters[1].type==ParameterType::ANY)
		{
			right = PKB::shortcutTable.GetAllStatement().size();
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::LINE)
		{
			right = 1;
		}
		value = _heuristics["PARENT_T"]*left*right;
	}
	else if (relation.type==RelationType::NEXT)
	{
		
		if (relation.parameters[0].type==ParameterType::ANY)
		{
			left = PKB::shortcutTable.GetAllStatement().size();
		}
		else if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}
		else if (relation.parameters[0].type==ParameterType::LINE)
		{
			left = 1;
		}

		if (relation.parameters[1].type==ParameterType::ANY)
		{
			right = PKB::shortcutTable.GetAllStatement().size();
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::LINE)
		{
			right = 1;
		}
		value = _heuristics["NEXT"]*left*right;
	}
	else if (relation.type==RelationType::NEXT_T)
	{
		
		if (relation.parameters[0].type==ParameterType::ANY)
		{
			left = PKB::shortcutTable.GetAllStatement().size();
		}
		else if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}
		else if (relation.parameters[0].type==ParameterType::LINE)
		{
			left = 1;
		}

		if (relation.parameters[1].type==ParameterType::ANY)
		{
			right = PKB::shortcutTable.GetAllStatement().size();
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::LINE)
		{
			right = 1;
		}
		value = _heuristics["NEXT_T"]*left*right;
	}
	else if (relation.type==RelationType::AFFECTS)
	{
		
		if (relation.parameters[0].type==ParameterType::ANY)
		{
			left = PKB::shortcutTable.GetAllAssign().size();
		}
		else if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}
		else if (relation.parameters[0].type==ParameterType::LINE)
		{
			left = 1;
		}

		if (relation.parameters[1].type==ParameterType::ANY)
		{
			right = PKB::shortcutTable.GetAllAssign().size();
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::LINE)
		{
			right = 1;
		}
		value = _heuristics["AFFECTS"]*left*right;
	}
	else if (relation.type==RelationType::AFFECTS_T)
	{
		
		if (relation.parameters[0].type==ParameterType::ANY)
		{
			left = PKB::shortcutTable.GetAllAssign().size();
		}
		else if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}
		else if (relation.parameters[0].type==ParameterType::LINE)
		{
			left = 1;
		}

		if (relation.parameters[1].type==ParameterType::ANY)
		{
			right = PKB::shortcutTable.GetAllAssign().size();
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::LINE)
		{
			right = 1;
		}
		value = _heuristics["AFFECTS_T"]*left*right;
	}
	else if (relation.type==RelationType::PATTERN_A)
	{
		
		if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}

		if (relation.parameters[1].type==ParameterType::ANY)
		{
			//no restriction, in implementation of pattern_a, we do not even check it
			right = 1;
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::NAME)
		{
			right = 1;
		}
		value = _heuristics["PATTERN_A"]*left*right;
	}
	else if (relation.type==RelationType::PATTERN_I)
	{
		
		if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}

		if (relation.parameters[1].type==ParameterType::ANY)
		{
			//no restriction, in implementation of pattern_i, we do not even check it
			right = 1;
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::NAME)
		{
			right = 1;
		}
		value = _heuristics["PATTERN_I"]*left*right;
	}
	else if (relation.type==RelationType::PATTERN_W)
	{
		
		if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}

		if (relation.parameters[1].type==ParameterType::ANY)
		{
			//no restriction, in implementation of pattern_i, we do not even check it
			right = 1;
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::NAME)
		{
			right = 1;
		}
		value = _heuristics["PATTERN_W"]*left*right;
	}
	else if (relation.type==RelationType::MODIFIES_S)
	{
		
		if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}
		else if (relation.parameters[0].type==ParameterType::LINE)
		{
			left = 1;
		}
		if (relation.parameters[1].type==ParameterType::ANY)
		{
			right = PKB::varTable.GetAllVarIndex().size();
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::NAME)
		{
			right = 1;
		}
		value = _heuristics["MODIFIES_S"]*left*right;
	}
	else if (relation.type==RelationType::MODIFIES_P)
	{
		
		if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}
		else if (relation.parameters[0].type==ParameterType::NAME)
		{
			left = 1;
		}
		if (relation.parameters[1].type==ParameterType::ANY)
		{
			right = PKB::varTable.GetAllVarIndex().size();
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::NAME)
		{
			right = 1;
		}
		value = _heuristics["MODIFIES_P"]*left*right;
	}
	else if (relation.type==RelationType::USES_S)
	{
		
		if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}
		else if (relation.parameters[0].type==ParameterType::NAME)
		{
			left = 1;
		}
		else if (relation.parameters[0].type==ParameterType::LINE)
		{
			left = 1;
		}
		if (relation.parameters[1].type==ParameterType::ANY)
		{
			right = PKB::varTable.GetAllVarIndex().size();
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::NAME)
		{
			right = 1;
		}
		value = _heuristics["USES_S"]*left*right;
	}
	else if (relation.type==RelationType::USES_P)
	{
		
		if (relation.parameters[0].type==ParameterType::ENTITY)
		{
			left = _synonyms[relation.parameters[0].value];
		}
		else if (relation.parameters[0].type==ParameterType::NAME)
		{
			left = 1;
		}
		else if (relation.parameters[0].type==ParameterType::LINE)
		{
			left = 1;
		}
		if (relation.parameters[1].type==ParameterType::ANY)
		{
			right = PKB::varTable.GetAllVarIndex().size();
		}
		else if (relation.parameters[1].type==ParameterType::ENTITY)
		{
			right = _synonyms[relation.parameters[1].value];
		}
		else if (relation.parameters[1].type==ParameterType::NAME)
		{
			right = 1;
		}
		value = _heuristics["USES_P"]*left*right;
	}
	return value;
}

bool QueryPreprocessor::DequeueRelation( query_relation& relation )
{
	if ( this->_tree.relations.empty() )
		return false;

	auto pos = this->_tree.relations.begin();
	unsigned __int64 minScore = std::numeric_limits<unsigned __int64>::max();

	for ( auto r = this->_tree.relations.begin(); r != this->_tree.relations.end(); r++ )
	{
		unsigned __int64 score = ScoreRelation( *r );
		
		if ( score < minScore )
		{
			pos = r;
			minScore = score;			
		}
	}

	relation = *pos;
	this->_tree.relations.erase( pos );

	return true;
}

void QueryPreprocessor::Update( std::string synonym, int value )
{
	this->_synonyms[ synonym ] = value;
}

void QueryPreprocessor::Preprocess( query_tree& tree )
{
	this->_tree = tree;
	this->_synonyms.clear();

	for ( auto variable = tree.variables.begin(); variable != tree.variables.end(); variable++ )
	{
		// init each entity count based on shortcut table
		switch( variable->type )
		{
			case kSTMT:
				this->_synonyms[ variable->name ] = PKB::shortcutTable.GetStatementCount();
				break;

			case kPROG_LINE:
				this->_synonyms[ variable->name ] = PKB::shortcutTable.GetProgramLineCount();
				break;

			case kIF:
				this->_synonyms[ variable->name ] = PKB::shortcutTable.GetIfCount();
				break;

			case kWHILE:
				this->_synonyms[ variable->name ] = PKB::shortcutTable.GetWhileCount();
				break;

			case kASSIGN:
				this->_synonyms[ variable->name ] = PKB::shortcutTable.GetAssignCount();
				break;

			case kCONSTANT:
				this->_synonyms[ variable->name ] = PKB::shortcutTable.GetConstantCount();
				break;

			case kCALL:
				this->_synonyms[ variable->name ] = PKB::shortcutTable.GetCallCount();
				break;

			case kVARIABLE:
				this->_synonyms[ variable->name ] = PKB::varTable.GetVarCount();
				break;

			case kPROCEDURE:
				this->_synonyms[ variable->name ] = PKB::procTable.GetProcCount();
				break;

			default:
				std::cout << "No metric detected for entity type: " << variable->type << std::endl;
				this->_synonyms[ variable->name ] = 0;
				break;
		}
	}

}